﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using EF_Model;
using Moq;
using System.Data.Objects;
using System;
using System.Collections.Generic;

namespace EF_Tests
{
    /// <summary>
    /// Really useful for
    /// 1. REPOSITORY TEST: if we need to test the query logic against in-memory objects rather loading from database
    /// 2. Business Logic/Controller Test: ??
    /// </summary>
    [TestClass]
    public class RepositoryTest_Read
    {
        [TestMethod]
        public void Read_Test_With_Concrete_Context_And_Default_ObjectSet_Implementation()
        {
            DatabaseContext dbContext = new DatabaseContext();
            CustomerRepository cr = new CustomerRepository(dbContext);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            dbContext.Customers.AddObject(c);
            //AddObject will not a object searchable by LINQ queries unless dbContext.SaveChanges() is called/physically written.
            
            //dbContext.SaveChanges();

            Console.Write(cr.GetById("118").ContactName);//fails
        }

        [TestMethod]
        public void Read_Test_With_Concrete_Mock_Context_And_Default_ObjectSet_Implementation()
        {
            Mock<DatabaseContext> dbContext = new Mock<DatabaseContext>();
            CustomerRepository cr = new CustomerRepository(dbContext.Object);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            ObjectSet<Customer> customerObjectSet = dbContext.Object.CreateObjectSet<Customer>();
            customerObjectSet.AddObject(c);

            //customerObjectSet is not Connected with dbContext in any way !!
            
            Console.Write(cr.GetById("118").ContactName);//fails
        }

        [TestMethod]
        public void Read_Test_With_Concrete_Mock_Context_And_Virtual_ObjectSet_Implementation_With_Mock_Setup_To_Make_It_Connected()
        {
            Mock<DatabaseContext> dbContext = new Mock<DatabaseContext>();
            CustomerRepository cr = new CustomerRepository(dbContext.Object);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            ObjectSet<Customer> customerObjectSet = dbContext.Object.CreateObjectSet<Customer>();

            /*
             * since customerObjectSet is being created from dbContext.Object.CreateObjectSet, which is a context that is bound physically, 
             * thus, future search will be happened physically.
             * Also, customerObjectSet.AddObject(c) will not be searchable as it has not been committed with the physical database. 
             * 
             */

            customerObjectSet.AddObject(c);//<< -- AddObject is not really adding the object to be searchable !!!! -- >>
            
            //dbContext.Setup(db => db.Customers).Returns(customerObjectSet);//the default implementation throws error:  Invalid setup on a non-overridable member: db => db.Customers
            //to remove this error we have to [[ declare the dbContext.Customers as virtual ]]
            //However still we can't get the mock data
            //customerObjectSet.AddObject(c) is not really adding the object.
            //this may because the dbContext is a mock object. 
            //To add it a concrete implementation is needed for dbContext to be performed in DB level
            //or a fake class is needed to be performed in-memory level
            
            Console.Write(cr.GetById("118").ContactName);//fails
            /* GetById actually searches in physical database!!! */
        }

        [TestMethod]
        /*
        RECOMMENED
        */
        public void Read_Test_With_Concrete_Mock_Context_And_Fake_ObjectSet_Implementation_With_Mock_Setup()
        {
            Mock<DatabaseContext> dbContext = new Mock<DatabaseContext>();
            CustomerRepository cr = new CustomerRepository(dbContext.Object);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            var customerObjectSet = new FakeObjectSet<Customer>();
            customerObjectSet.AddObject(c);

            dbContext.Setup(db => db.Customers).Returns(customerObjectSet);
            //to remove the issue in the above test, we introduce a new class FakeObjectSet, which is an implementation of IObjectSet
            //to make it mock-set-uppable 
            //[[ we need to update db.Customers to have return type IObjectSet ]]

            Console.Write(cr.GetById("118").ContactName);//passes
        }
    }
}

